package com.bo.day20241113;


import java.util.Arrays;
import java.util.Comparator;

/**
 * 一些项目要占用一个会议室宣讲，会议室不能同时容纳两个项目的宣讲。
 * 给你每一个项目开始的时间和结束的时间
 * 你来安排宣讲的日程，要求会议室进行的宣讲的场次最多。
 * 返回最多的宣讲场次
 * 暴力递归枚举递归图解：https://cloud.fynote.com/share/d/NHIIffLiD
 *
 * @Author: gpb
 * @Date: 2024/11/13 9:42
 * @Description:
 */
public class BestArrange {


    // for test
    public static Program[] generatePrograms(int programSize, int timeMax) {
        Program[] ans = new Program[(int) (Math.random() * (programSize + 1))];
        for (int i = 0; i < ans.length; i++) {
            int r1 = (int) (Math.random() * (timeMax + 1));
            int r2 = (int) (Math.random() * (timeMax + 1));
            if (r1 == r2) {
                ans[i] = new Program(r1, r1 + 1);
            } else {
                ans[i] = new Program(Math.min(r1, r2), Math.max(r1, r2));
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        int programSize = 12;
        int timeMax = 20;
        int timeTimes = 1000000;
        for (int i = 0; i < timeTimes; i++) {
            Program[] programs = generatePrograms(programSize, timeMax);
            if (bestArrange1(programs) != bestArrange(programs)) {
                System.out.println("Oops!");
            }
        }
        System.out.println("finish!");
    }


    public static class Program {
        public int start;
        public int end;

        public Program (int start, int end) {
            this.start = start;
            this.end = end;
        }
    }

    /**
     * 定义比较器
     */
    public static class ProgramComparator implements Comparator<Program> {
        @Override
        public int compare (Program o1, Program o2) {
            return o1.end - o2.end;
        }
    }

    /**
     * 贪心解题思路：
     * 1. 按照结束时间进行升序排序
     * 2. 遍历数组,如果上一个结束时间小于当前开始时间，则可以进行宣讲，否则不能
     *
     * @param programs
     * @return
     */
    public static int bestArrange (Program[] programs) {
        if (programs == null || programs.length == 0) {
            return 0;
        }
        // 按照结束时间进行升序排序
        Arrays.sort(programs, new ProgramComparator());
        int result = 0;
        int times = 0;
        for (Program program : programs) {
            // 如果上一个结束时间小于当前开始时间
            if (program.start >= times) {
                // 可以进行宣讲
                result++;
                // 更新下次会议最早开始时间，就是上个会议的结束时间
                times = program.end;
            }
        }
        return result;
    }

    /**
     * 暴力递归解法
     *
     * @param programs
     * @return
     */
    public static int bestArrange1 (Program[] programs) {
        if (programs == null || programs.length == 0) {
            return 0;
        }
        return process(programs, 0, 0);
    }

    /**
     * @param arr       待安排的会议集合
     * @param done      已安排会议的次数
     * @param timesLine 已安排的时间线,上次会议的结束时间
     * @return
     */
    public static int process (Program[] arr, int done, int timesLine) {
        if (arr.length == 0) {
            // 没有会议了，返回已安排的会议次数
            return done;
        }
        // 记录最大安排会议的次数
        int max = done;
        // 开始循环会议
        for (int i = 0; i < arr.length; i++) {
            // 当前会议的开始时间小于等于上次会议的结束时间，可以安排，依次枚举
            if (arr[i].start >= timesLine) {
                max = Math.max(max, process(copyButExcept(arr, i), done + 1, arr[i].end));
            }
        }
        return max;
    }

    /**
     * 拷贝数组，除了i位置的元素
     *
     * @param programs
     * @param i
     * @return
     */
    public static Program[] copyButExcept (Program[] programs, int i) {
        Program[] arr = new Program[programs.length - 1];
        int index = 0;
        for (int k = 0; k < programs.length; k++) {
            if (k != i) {
                arr[index++] = programs[k];
            }
        }
        return arr;
    }
}
